﻿using System.Linq;
using PerfAnalyzerLib;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;

namespace UnitTests
{
    
    
    /// <summary>
    ///This is a test class for CounterStatsTest and is intended
    ///to contain all CounterStatsTest Unit Tests
    ///</summary>
	[TestClass]
	public class CounterStatsTest
	{
		private TestContext testContextInstance;

		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext
		{
			get
			{
				return testContextInstance;
			}
			set
			{
				testContextInstance = value;
			}
		}

		#region Additional test attributes
		// 
		//You can use the following additional attributes as you write your tests:
		//
		//Use ClassInitialize to run code before running the first test in the class
		//[ClassInitialize()]
		//public static void MyClassInitialize(TestContext testContext)
		//{
		//}
		//
		//Use ClassCleanup to run code after all tests in a class have run
		//[ClassCleanup()]
		//public static void MyClassCleanup()
		//{
		//}
		//
		//Use TestInitialize to run code before running each test
		//[TestInitialize()]
		//public void MyTestInitialize()
		//{
		//}
		//
		//Use TestCleanup to run code after each test has run
		//[TestCleanup()]
		//public void MyTestCleanup()
		//{
		//}
		//
		#endregion

		/// <summary>
		/// Verify exception with null array.
		/// </summary>
		[TestMethod]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CounterStats_CalculateStats_ArgumentNullExceptionTest()
		{
			var target = new CounterStats(null);
		}

		/// <summary>
		/// Verify throwing exception with empty array.
		/// </summary>
		[TestMethod]
		[ExpectedException(typeof(ArgumentException))]
		public void CounterStats_CalculateStats_ArgumentExceptionTest()
		{
			var target = new CounterStats(new double[] {});
		}

		/// <summary>
		/// Verify with an array of size one.
		/// </summary>
		[TestMethod]
		public void CounterStats_CalculateStats_SingleValueTest()
		{
			const double singleValue = 55.55;
			var values = new[] { singleValue };
			var target = new CounterStats(values);

			Assert.AreEqual(singleValue, target.Min);
			Assert.AreEqual(singleValue, target.Max);
			Assert.AreEqual(singleValue, target.Average);
			Assert.AreEqual(singleValue, target.Percentile25th);
			Assert.AreEqual(singleValue, target.Percentile50th);
			Assert.AreEqual(singleValue, target.Percentile75th);
			Assert.AreEqual(singleValue, target.Percentile90th);
			Assert.AreEqual(singleValue, target.Percentile95th);
			Assert.AreEqual(singleValue, target.Percentile99th);
		}

		/// <summary>
		/// Verify Average, Min and Max.
		/// </summary>
		[TestMethod]
		public void CounterStats_CalculateStats_AverageMinMaxTest()
		{
			const double min = -15;
			const double max = 15;
			var values = new[] { max, min };

			// TODO verify perf compared to for loop. Try PLINQ as well.
			double sum = values.Sum();

			double expectedAverage = sum / values.Length;

			var target = new CounterStats(values);

			Assert.AreEqual(expectedAverage, target.Average);
			Assert.AreEqual(min, target.Min);
			Assert.AreEqual(max, target.Max);
		}

		/// <summary>
		/// Verrify percentiles.
		/// </summary>
		[TestMethod]
		public void CounterStats_CalculateStats_PercentilesTest()
		{
			const int arraySize = 100;

			var tempValues = new List<double>();

			for (int i = 0; i < arraySize; i++)
			{
				tempValues.Add(i);
			}

			double[] values = tempValues.ToArray();

			var target = new CounterStats(values);

			Assert.AreEqual(arraySize * 0.25, target.Percentile25th);
			Assert.AreEqual(arraySize * 0.50, target.Percentile50th);
			Assert.AreEqual(arraySize * 0.75, target.Percentile75th);
			Assert.AreEqual(arraySize * 0.90, target.Percentile90th);
			Assert.AreEqual(arraySize * 0.95, target.Percentile95th);
			Assert.AreEqual(arraySize * 0.99, target.Percentile99th);
		}
	}
}
